package sk.tuke.constraintbuilder.editor;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.eclipse.jface.dialogs.MessageDialog;

import sk.tuke.constraintbuilder.handler.CallEditor;
import sk.tuke.constraintbuilder.handler.ListRefresh;
import sk.tuke.constraintbuilder.model.Annotation;
import sk.tuke.constraintbuilder.model.Annotations;
import sk.tuke.constraintbuilder.model.Choice;
import sk.tuke.constraintbuilder.model.ConditionsElement;
import sk.tuke.constraintbuilder.model.Constraint;
import sk.tuke.constraintbuilder.model.ConstraintPatterns;
import sk.tuke.constraintbuilder.model.ConstraintPatterns.ConstraintPattern;
import sk.tuke.constraintbuilder.model.ConstraintPatterns.ContentType;
import sk.tuke.constraintbuilder.model.ConstraintPatterns.RuleType;
import sk.tuke.constraintbuilder.model.Context;
import sk.tuke.constraintbuilder.model.IElementOfStructuredElement;
import sk.tuke.constraintbuilder.model.ILeaf;
import sk.tuke.constraintbuilder.model.INodeElement;
import sk.tuke.constraintbuilder.model.Model;
import sk.tuke.constraintbuilder.model.Not;
import sk.tuke.constraintbuilder.model.RulesElement;
import sk.tuke.constraintbuilder.model.RuleElement;
import sk.tuke.constraintbuilder.model.Severity;
import sk.tuke.constraintbuilder.model.SimpleRuleElement;
import sk.tuke.constraintbuilder.model.StructuredRuleElement;
import sk.tuke.constraintbuilder.model.Template;
import sk.tuke.constraintbuilder.model.Value;
import sk.tuke.constraintbuilder.utils.MyUtils;
import sk.tuke.constraintbuilder.utils.XMLUtils;
import sk.tuke.constraintbuilder.views.ConstraintEditorView;

public class ConstraintEditorController implements IConstraintController{
	
	private Constraint constraint;
	private ConstraintEditorView view;
	private ConstraintEditor editor;
	private ConstraintPatterns constraintPatterns;

	public ConstraintEditorController(ConstraintEditor editor) {
		this.editor = editor;
		EditorInput editorInput = editor.getEditorInput();
		this.constraint = (Constraint) editorInput.getInputElement();
		try {
			constraintPatterns = MyUtils.getConstraintPatterns();
		} catch (JAXBException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	}
	
	public void setView(ConstraintEditorView view){
		this.view = view;
		view.showConstraint(constraint);
	}

	public void saveConstraint(String name, String annotationName, String context, String severity, String description) {
		constraint.setName(name);
		constraint.setAnnotationName(annotationName);
		constraint.setContext(Context.getContext(context));
		constraint.setSeverity(Severity.getSeverity(severity));
		constraint.setDescription(description);
		Model.getModel().addConstraint(constraint);
		Model.getModel().save(constraint);
		MyUtils.executeCommand(ListRefresh.COMMAND, editor.getSite());
		editor.getParent().refresh(new EditorInput(constraint));
	}

	public void addRuleElementToRules(String ruleType, String ruleName) {
		RuleElement element = createRuleElement(ruleType, ruleName);
		if(element!=null){
			constraint.getRules().addElement(element);
			view.addRulePanel(element);
		}
	}
	
	public void addRuleElementToConditions(String ruleType, String ruleName) {
		RuleElement element = createRuleElement(ruleType, ruleName);
		if(element!=null){
			if(constraint.getConditions()==null){
				constraint.setConditions(new ConditionsElement());
			}
			constraint.getConditions().addElement(element);
			view.addCondPanel(element);
		}
	}
	
	private RuleElement createRuleElement(String ruleType, String ruleName){
		ConstraintPattern constraintPattern = constraintPatterns.getPattern(ruleName);
		if(constraintPattern!=null){
			RuleElement ruleElement;
			if(constraintPattern.getRuleType()==RuleType.SIMPLE){
				ruleElement = new SimpleRuleElement();
				ruleElement.setName(ruleName);
				ruleElement.setDescription(constraintPattern.getDescription());
				return ruleElement;
			}
			else if(constraintPattern.getRuleType()==RuleType.STRUCTURED){
				ruleElement = new StructuredRuleElement();
				ruleElement.setName(ruleName);
				ruleElement.setDescription(constraintPattern.getDescription());
				IElementOfStructuredElement subElement = null;
				if(ruleType.equals("NEGATIVE"))subElement = new Not();
				else if(ruleType.equals("CHOICE"))subElement = new Choice();
				if(subElement!=null){
					((StructuredRuleElement)ruleElement).setElement(subElement);
				}
				else if(constraintPattern.getContent()==ContentType.ANNOTATION_CONTAINER){
					((StructuredRuleElement)ruleElement).setElement(new Annotations());
				}	
				else ((StructuredRuleElement)ruleElement).setElement(new Value());
				return ruleElement;
			}
		}
		return null;
	}
	
	public void setElementValues(RuleElement element, String[] values) {
		if(element instanceof StructuredRuleElement){
			IElementOfStructuredElement subElement = ((StructuredRuleElement)element).getElement();
			if(subElement!=null){
				if(subElement instanceof Value){
					((Value) subElement).setValue(values[0]);
				}
				else{
					List<ILeaf> leaves = new ArrayList<>();
					for (String value : values) {
						if(constraintPatterns.getContentType(element.getName())==ContentType.ANNOTATION_CONTAINER){
							leaves.add(new Annotation(value));
						}
						else leaves.add(new Value(value));
					}
					if(subElement instanceof INodeElement)
						((INodeElement) subElement).setElements(leaves);
				}
			}
		}
	}

	public void removeRuleElement(RuleElement element) {
		constraint.removeElement(element);
	}
	
	public void callTemplateEditor(String parameter){
		MyUtils.executeParametrizedCommand(CallEditor.COMMAND, CallEditor.PARAMETER,  parameter, editor.getSite());
	}
	
	public ConstraintPatterns getConstraintPatterns(){
		return constraintPatterns;
	}

	/**
	 * Set existing template as constraint model
	 * @param id Template ID
	 */
	public void setTemplate(int id) {
		File file = Model.getModel().getFile(id);
		if(file!=null){
			Template template = XMLUtils.xmlToTemplate(file);
			RulesElement rule = new RulesElement();
			ConditionsElement condition = new ConditionsElement();
			for (RuleElement element : template.getRule().getElements()) {
				rule.addElement(element);
			}
			for (RuleElement element : template.getCondition().getElements()) {
				condition.addElement(element);
			}
			constraint.setRules(rule);
			constraint.setConditions(condition);
			view.showConstraint(constraint);
		}
		else{
			MessageDialog.openError(editor.getSite().getShell(), "Template Error", "Template file not found!");
		}
	}
	
	/**
	 * Create template from existing constraint and open template editor
	 */
	public void createNewTemplateFromConstraint(){
		File file = Model.getModel().getFile(constraint.getId());
		if(file!=null){
			Constraint constraint = XMLUtils.xmlToConstraint(file);
			constraint.deleteAllValues();
			RulesElement rule = new RulesElement();
			ConditionsElement condition = new ConditionsElement();
			for (RuleElement element : constraint.getRules().getElements()) {
				rule.addElement(element);
			}
			for (RuleElement element : constraint.getConditions().getElements()) {
				condition.addElement(element);
			}
			Template template = new Template();
			template.setRule(rule);
			template.setCondition(condition);
			Model.getModel().addTemplate(template);
			Model.getModel().save(template);
			callTemplateEditor(String.valueOf(template.getId()));
		}
		else{
			MessageDialog.openError(editor.getSite().getShell(), "Constraint Error", "Constraint file not found! You must save file before creating template.");
		}
	}
}
